
#include "tar.h"
#include "disc.h"

static DiscRec MyDisc;
static int DiscDrive;
static char *DiscSpec = ":x";
static int DiscSize;
static char *CylinderBuffer;
static int SecSize;
static int TrackSize;
static int CylinderSize;
static int CylinderIndex;
static int DiscAddress;
static int Start;
static int Cylinder;
static int NumCylinders;


void DescribeDisc(int Drive) {
  DiscSpec[1] = Drive+'0';
  chkos(os_swi2(os_X | ADFS_DescribeDisc, (int)DiscSpec, (int)&MyDisc));
  DiscDrive = Drive;
  DiscAddress = adfs_Offset;
} /* DescribeDisc */


void InitDisc() {
  DiscAddress = 0;
  Cylinder = 0;
  CylinderIndex = 0;
  Start = 1;
} /* InitDisc */


void SetupDisc(int Drive, char Format) {
  NumCylinders = 80;
  DiscSpec[1] = Drive+'0';
  MyDisc.Density = 2;
  MyDisc.NumHeads = 2;
  MyDisc.LowSector = 0;
  switch (Format) {
    case 'E':
    case 'D':
      MyDisc.Log2SecSize = 10;
      MyDisc.SecsPerTrack = 5;
      break;
    case 'F':
      MyDisc.Log2SecSize = 10;
      MyDisc.SecsPerTrack = 10;
      MyDisc.Density = 4;
      break;
    case 'L':
      MyDisc.Log2SecSize = 8;
      MyDisc.SecsPerTrack = 16;
      break;
    case 'M':
      MyDisc.LowSector = 1;
      MyDisc.Log2SecSize = 9;
      MyDisc.SecsPerTrack = 9;
      break;
    case 'Q':
      MyDisc.LowSector = 1;
      MyDisc.Log2SecSize = 9;
      MyDisc.SecsPerTrack = 18;
      MyDisc.Density = 4;
      break;
    case 'H':
      MyDisc.LowSector = 1;
      MyDisc.Log2SecSize = 9;
      MyDisc.SecsPerTrack = 15;
      break;
    case 'N':
      MyDisc.LowSector = 1;
      NumCylinders = 40;
      MyDisc.Log2SecSize = 9;
      MyDisc.SecsPerTrack = 9;
      break;
  }
  SecSize = 1 << MyDisc.Log2SecSize;
  MyDisc.DiscSize =
    (long)SecSize * (long)MyDisc.SecsPerTrack *
    (long)MyDisc.NumHeads * (long)NumCylinders;
  TrackSize = SecSize * MyDisc.SecsPerTrack;
  CylinderSize = TrackSize * MyDisc.NumHeads;
  DiscSize = CylinderSize * NumCylinders;
  DiscDrive = Drive;
  CylinderBuffer = (char *)malloc(CylinderSize);
  DiscAddress = 0;
  Cylinder = 0;
  CylinderIndex = 0;
  Start = 1;
  DiscNo = 1;
} /* SetupDisc */


void CylinderOp(int Operation) {
  int Head;
  os_regset regs;

  for (Head = 0; Head < MyDisc.NumHeads; Head++) {
    do {
      regs.r[1] = Operation | (1 << 6) | ((int)&MyDisc << 6);
      regs.r[2] = (SecSize*(MyDisc.SecsPerTrack
                                      *(MyDisc.NumHeads*Cylinder+Head)))
                  | (DiscDrive << 29);
      regs.r[3] = (int)CylinderBuffer+Head*TrackSize;
      regs.r[4] = TrackSize;
    } while (!chkos(os_swix(ADFS_DiscOp,&regs)));
  }
  Cylinder++;
  CylinderIndex = 0;
  Start = 0;
} /* CylinderOp */


int WriteDisc(char *Buffer, long Size) {
  int ToWrite, ToCopy;

  ToWrite = (int)Size;
  do {
    ToCopy = CylinderSize - CylinderIndex;
    if (ToCopy > ToWrite)
      ToCopy = ToWrite;
    memcpy(CylinderBuffer + CylinderIndex, Buffer, ToCopy);
    Buffer += ToCopy;
    CylinderIndex += ToCopy;
    ToWrite -= ToCopy;
    if (CylinderIndex >= CylinderSize)
      CylinderOp(adfs_WriteSectors);
  } while (ToWrite > 0);
  DiscAddress += (int)Size;
  return((int)Size);
} /* WriteDisc */


int ReadDisc(char *Buffer, long Size) {
  int ToRead, ToCopy;

  ToRead = (int)Size;
  do {
    if (!Start) {
      ToCopy = CylinderSize - CylinderIndex;
      if (ToCopy > ToRead)
        ToCopy = ToRead;
      memcpy(Buffer,CylinderBuffer+CylinderIndex,ToCopy);
      Buffer += ToCopy;
      CylinderIndex += ToCopy;
      ToRead -= ToCopy;
    }
    if (ToRead > 0 && CylinderIndex >= CylinderSize || Start) {
      if (Cylinder < NumCylinders) {
        CylinderOp(adfs_ReadSectors);
      } else {
        fprintf(stderr,"Please insert disc #%d:",++DiscNo);
        while (getchar() != '\n')
          ;
        InitDisc();
        CylinderOp(adfs_ReadSectors);
        CylinderIndex += RECORDSIZE;
        DiscAddress += RECORDSIZE;
      }
    }
  } while (ToRead > 0);
  DiscAddress += (int)Size;
  return((int)Size);
} /* ReadDisc */


long FreeOnDisc(void) {
  return((long)DiscSize-(long)DiscAddress);
} /* StillOnDisc */


void FlushDisc(void) {
  if (CylinderIndex > 0)
    CylinderOp(adfs_WriteSectors);
} /* FlushDisc */


void BackDisc(void) {
  int NumBytesBack,NumCylindersBack,NewDiscAddress;

  NumBytesBack = NumBlocksRead*RECORDSIZE;
  NumCylindersBack = (NumBytesBack+CylinderSize-1)/CylinderSize;
  Cylinder -= NumCylindersBack;
  CylinderOp(adfs_ReadSectors);
  Cylinder--;
  NewDiscAddress = Cylinder*CylinderSize;
  CylinderIndex = DiscAddress-NumBytesBack-NewDiscAddress;
  DiscAddress = NewDiscAddress;
} /* BackDisc */


void FormatDisc(void) {
  os_cli("adfs:format 0 e y");
} /* FormatDisc */
